package wpmp.security.client.log;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.Appender;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import java.io.File;
import java.util.Enumeration;
import java.util.Properties;

public class AppContextLogFactory {

	private static AppContextLogFactory HOLDER = new AppContextLogFactory();

	static final String WPMP_SECURITY_CLIENT = "WpmpSecurityClient";

	static final String LOG_NAME_WPMP_SECURITY_CLIENT = WPMP_SECURITY_CLIENT + "Log";

	static final String LOG_NAME_WPMP_SECURITY_CLIENT_FILE = LOG_NAME_WPMP_SECURITY_CLIENT + "FILE";

	static final Log log = LogFactory.getLog(AppContextLogFactory.class);

	private volatile boolean initOK = false;

	private String loggerDir = System.getProperty("user.home") + File.separator + "logs";

	private AppContextLogFactory() {
	}

	public static AppContextLogFactory holder() {
		return HOLDER;
	}

	public AppContextLogFactory setLoggerDir(String loggerDir) {
		if (StringUtils.isNotBlank(loggerDir)) {
			this.loggerDir = loggerDir;
		}

		if (log.isInfoEnabled()) {
			log.info("loggerDir=" + loggerDir);
		}

		return HOLDER;
	}

	String getLoggerFile() {
		return this.loggerDir + File.separator + WPMP_SECURITY_CLIENT + ".log";
	}

	Properties newProperties() {
		final Properties properties = new Properties();

		properties.put("log4j.logger." + LOG_NAME_WPMP_SECURITY_CLIENT, "info, " + LOG_NAME_WPMP_SECURITY_CLIENT_FILE);

		properties.put("log4j.additivity." + LOG_NAME_WPMP_SECURITY_CLIENT, "false");

		properties.put("log4j.appender." + LOG_NAME_WPMP_SECURITY_CLIENT_FILE,
				"org.apache.log4j.DailyRollingFileAppender");

		properties.put("log4j.appender." + LOG_NAME_WPMP_SECURITY_CLIENT_FILE + ".DatePattern", "'.'yyyy-MM-dd");

		properties.put("log4j.appender." + LOG_NAME_WPMP_SECURITY_CLIENT_FILE + ".File", WPMP_SECURITY_CLIENT + ".log");

		properties.put("log4j.appender." + LOG_NAME_WPMP_SECURITY_CLIENT_FILE + ".layout",
				"org.apache.log4j.PatternLayout");

		properties.put("log4j.appender." + LOG_NAME_WPMP_SECURITY_CLIENT_FILE + ".layout.ConversionPattern",
				"[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} %l %m %n");

		properties.put("log4j.appender." + LOG_NAME_WPMP_SECURITY_CLIENT_FILE + ".Append", "true");

		return properties;
	}

	public AppContextLogFactory init() {

		if (initOK) {
			return HOLDER;
		}

		final ClassLoader cl = Thread.currentThread().getContextClassLoader();
		Thread.currentThread().setContextClassLoader(log.getClass().getClassLoader());

		try {
			// 使缺省的配置生效(Logger, Appender)
			PropertyConfigurator.configure(newProperties());

			/**
			 * <pre>
			 * 找到上层应用在 Root Logger 上设置的 FileAppender，以及 wpmp 配置的 FileAppender。 
			 * 目的是为了让 wpmp 的日志与上层应用的日志输出到同一个目录。
			 * </pre>
			 */
			FileAppender bizFileAppender = getFileAppender(Logger.getRootLogger());
			if (null == bizFileAppender) {
				log.warn("上层业务层没有在ROOT LOGGER上设置FileAppender!!!!");

				bizFileAppender = new FileAppender();
				bizFileAppender.setFile(this.getLoggerFile());
			}

			setFileAppender(bizFileAppender, LOG_NAME_WPMP_SECURITY_CLIENT);

			initOK = true;
		}
		catch (Throwable e) {
			log.warn("Failed to init " + LOG_NAME_WPMP_SECURITY_CLIENT, e);
		}
		finally {
			Thread.currentThread().setContextClassLoader(cl);
		}

		return HOLDER;
	}

	static void setFileAppender(final FileAppender bizFileAppender, final String logName) {
		final FileAppender fileAppender = getFileAppender(Logger.getLogger(logName));
		final String bizLogDir = new File(bizFileAppender.getFile()).getParent();

		final File newLogFile = new File(bizLogDir, fileAppender.getFile());

		fileAppender.setFile(newLogFile.getAbsolutePath());
		fileAppender.activateOptions(); // 很重要，否则原有日志内容会被清空
		log.warn("成功为" + logName + "添加Appender. 输出路径:" + newLogFile.getAbsolutePath());
	}

	static FileAppender getFileAppender(final Logger logger) {
		FileAppender fileAppender = null;
		for (final Enumeration<?> appenders = logger.getAllAppenders(); (null == fileAppender)
				&& appenders.hasMoreElements();) {
			final Appender appender = (Appender) appenders.nextElement();
			if (FileAppender.class.isInstance(appender)) {
				fileAppender = (FileAppender) appender;
			}
		}
		return fileAppender;
	}

	static Log getLog() {
		return LogFactory.getLog(LOG_NAME_WPMP_SECURITY_CLIENT);
	}
}
